#include "Game Engine\Header\Body.h"
#include "Game Engine\Header\Camera.h"

Body::Body()
{
	mPosition			= D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
	mVelocity			= D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
	mConveyorVelocity	= D3DXVECTOR3( 0.0f, 0.0f, 0.0f );

	mMass	= 0.0f;
	mAngle	= 0.0f;
	mYaw	= 0.0f;
	mPitch	= 0.0f;
	mRoll	= 0.0f;
	mSpeed	= 10.0f;

	D3DXMatrixIdentity( &mWorld );
}

Body::~Body()
{
}

void Body::Init( ID3D10Device* device, btDynamicsWorld* world, std::wstring meshFile,
	ID3D10Effect* fx, ID3D10ShaderResourceView* cubeMap, float mass,
	Physics::Shape shape, bool useShadows )
{
	md3dDevice = device;
	mDynamicsWorld = world;

	mMesh = new Mesh;
	mMesh->Init( md3dDevice, meshFile, fx, useShadows );
	mMesh->SetCubeMap( cubeMap );

	mMass = mass;
}

void Body::Init( ID3D10Device* device, btDynamicsWorld* world, std::vector<std::wstring> meshFiles,
	ID3D10Effect* fx, ID3D10ShaderResourceView* cubeMap, float mass,
	Physics::Shape shape, bool useShadows )
{
	md3dDevice = device;
	mDynamicsWorld = world;

	for( UINT i = 0; i < meshFiles.size(); i ++ )
	{
		mMeshVector.push_back( new Mesh );
		mMeshVector[i]->Init( md3dDevice, meshFiles[i], fx, useShadows );
		mMeshVector[i]->SetCubeMap( cubeMap );
	}

	mMass = mass;

	mCompound = new btCompoundShape();

	btTransform trans;

	for( UINT i = 0; i < mMeshVector.size(); i ++ )
	{
		D3DXVECTOR3 loadAMin;
		D3DXVECTOR3 loadAMax;

		mMeshVector[i]->GetAABB( loadAMin, loadAMax );

		mWidth.push_back( abs( loadAMax.x - loadAMin.x ) / 2 );
		mHeight.push_back( abs( loadAMax.y - loadAMin.y ) / 2 );
		mLength.push_back( abs( loadAMax.z - loadAMin.z ) / 2 );

		btCollisionShape* loadShape = new btBoxShape( btVector3( mWidth[i],
			mHeight[i], mLength[i] ) );
		trans.setIdentity();
		mCompound->addChildShape( trans, loadShape );
	}
}

D3DXVECTOR3& Body::GetConveyorVelocity()
{
	mConveyorVelocity = BT2DX_VECTOR3( mBody->getConveyorVelocity() );

	return mConveyorVelocity;
}

void Body::SetConveyorVelocity( D3DXVECTOR3 vel )
{
	mConveyorVelocity = vel;
	
	mBody->setConveyorVelocity( BTVector3( mConveyorVelocity ) );
}

void Body::SetCompoundPosition( UINT base, UINT connection, D3DXVECTOR3 basePos, D3DXVECTOR3 connectionPos )
{
	if( ( mBody && mBody->getCollisionShape()->isCompound() ) || ( mGhostObject && mGhostObject->getCollisionShape()->isCompound() ) )
	{
		btCollisionShape* childBase = mCompound->getChildShape( base );
		btCollisionShape* childConnection = mCompound->getChildShape( connection );
		btVector3 baseMin;
		btVector3 baseMax;
		childBase->getAabb( mCompound->getChildTransform( base ), baseMin, baseMax );
		float baseWidth = abs( baseMax.x() - baseMin.x() );
		float baseHeight = abs( baseMax.y() - baseMin.y() );
		float baseLength = abs( baseMax.z() - baseMin.z() );

		float xBase = ( baseWidth / 2.0f ) * ( basePos.x / 100 );
		float yBase = ( baseHeight / 2.0f ) * ( basePos.y / 100 );
		float zBase = ( baseLength / 2.0f ) * ( basePos.z / 100 );

		btVector3 connectionMin;
		btVector3 connectionMax;
		childConnection->getAabb( mCompound->getChildTransform( connection ), connectionMin, connectionMax );
		float connectionWidth = abs( connectionMax.x() - connectionMin.x() );
		float connectionHeight = abs( connectionMax.y() - connectionMin.y() );
		float connectionLength = abs( connectionMax.z() - connectionMin.z() );

		float xConnection = ( connectionWidth / 2.0f ) * ( connectionPos.x / 100 );
		float yConnection = ( connectionHeight / 2.0f ) * ( connectionPos.y / 100 );
		float zConnection = ( connectionLength / 2.0f ) * ( connectionPos.z / 100 );

		float x = xConnection + xBase;
		float y = yConnection + yBase;
		float z = zConnection + zBase;

		btTransform trans;
		trans.setIdentity();
		trans.setOrigin( btVector3( x, y, z ) );

		mCompound->updateChildTransform( connection, trans );
	}
}

void Body::SetCompoundVelocity( UINT index, D3DXVECTOR3 vel )
{
	btTransform trans;
	trans.setIdentity();
	btVector3 pos = mCompound->getChildTransform( index ).getOrigin();
	trans.setOrigin( pos + BTVector3( vel ) );

	mCompound->updateChildTransform( index, trans );
}

D3DXVECTOR3 Body::GetRotation()
{
	return D3DXVECTOR3( mPitch, mYaw, mRoll );
}

void Body::SetRotation( Axis axis, float angle, bool clockwise )
{
	if( clockwise ? mAngle = -angle * ( PI / 180.0f ) : mAngle = angle * ( PI / 180.0f ) )
	if( mAngle >= 2 * PI )
		mAngle = 0.0f;

	btTransform trans;
	if( mBody )
		trans.setOrigin( mBody->getWorldTransform().getOrigin() );
	else if( mGhostObject )
		trans.setOrigin( mGhostObject->getWorldTransform().getOrigin() );
	switch( axis )
	{
	case X:
		mPitch = mAngle;
		break;
	case Y:
		mYaw = mAngle;
		break;
	case Z:
		mRoll = mAngle;
		break;
	}

	trans.setRotation( btQuaternion( mYaw, mPitch, mRoll ) );
	if( mBody )
		mBody->setWorldTransform( trans );
	else if( mGhostObject )
		mGhostObject->setWorldTransform( trans );
}

void Body::SetBounciness( float bounce )
{
	mBody ? mBody->setRestitution( bounce ) : mGhostObject->setRestitution( bounce );
}

void Body::SetGravity( D3DXVECTOR3 gravity )
{
	mBody->setGravity( BTVector3( gravity ) );
}

void Body::SetPlayerGravity( float gravity )
{
	mCharacter->setGravity( gravity );
}

void Body::SetMass( float mass )
{
	mMass = mass;

	btVector3 localInertia( 0, 0, 0 );
	if( mMass > 0.0f )
	{
		mBody ? mBody->getCollisionShape()->calculateLocalInertia( mMass, localInertia ) :
			mGhostObject->getCollisionShape()->calculateLocalInertia( mMass, localInertia );
	}
	if( mBody ) mBody->setMassProps( mMass, localInertia );
}

float Body::GetWidth( UINT meshIndex )
{
	D3DXVECTOR3 min;
	D3DXVECTOR3 max;

	if( mMeshVector.size() > 0 )
		mMeshVector[meshIndex]->GetAABB( min, max );
	else
		mMesh->GetAABB( min, max );

	return abs( max.x - min.x );
}

float Body::GetHeight( UINT meshIndex )
{
	D3DXVECTOR3 min;
	D3DXVECTOR3 max;

	if( mMeshVector.size() > 0 )
		mMeshVector[meshIndex]->GetAABB( min, max );
	else
		mMesh->GetAABB( min, max );

	return abs( max.y - min.y );
}

float Body::GetLength( UINT meshIndex )
{
	D3DXVECTOR3 min;
	D3DXVECTOR3 max;

	if( mMeshVector.size() > 0 )
		mMeshVector[meshIndex]->GetAABB( min, max );
	else
		mMesh->GetAABB( min, max );

	return abs( max.z - min.z );
}

D3DXMATRIX& Body::GetWorld()
{
	return mWorld;
}

btRigidBody* Body::GetRigidBody()
{
	return mBody;
}

btPairCachingGhostObject* Body::GetGhostObject()
{
	return mGhostObject;
}

btKinematicCharacterController* Body::GetCharacter()
{
	return mCharacter;
}

void Body::DrawToShadowMap( ID3D10EffectMatrixVariable* mfxBuildShadowMapLightWVPVar,
	ID3D10EffectShaderResourceVariable* mfxBuildShadowMapDiffuseMapVar,
	ID3D10EffectPass* pass,	D3DXMATRIX& lightViewProj )
{
	D3DXMATRIX WVP;
	if( ( mBody && mBody->getCollisionShape()->isCompound() ) ||
		( mGhostObject && mGhostObject->getCollisionShape()->isCompound() ) )
	{
		btCompoundShape* compound;
		compound = mBody ? ( btCompoundShape* )mBody->getCollisionShape() :
			( btCompoundShape* )mGhostObject->getCollisionShape();
		for( UINT i = 0; i < compound->getNumChildShapes(); i++ )
		{
			btTransform child = compound->getChildTransform(i);
			btTransform trans;
			trans = mBody ? mBody->getWorldTransform() * child :
				mGhostObject->getWorldTransform() * child;
			D3DXMATRIX W;
			W = BT2DX_MATRIX( trans );
			WVP = W * lightViewProj;
			mfxBuildShadowMapLightWVPVar->SetMatrix((float*)&WVP);
			mMeshVector[i]->DrawToShadowMap( mfxBuildShadowMapDiffuseMapVar, pass );
		}
	}
	else
	{
		WVP = mWorld * lightViewProj;
		mfxBuildShadowMapLightWVPVar->SetMatrix((float*)&WVP);
		mMesh->DrawToShadowMap( mfxBuildShadowMapDiffuseMapVar, pass );
	}
}

void Body::SetLight(const Light& light)
{
	if( mMeshVector.size() > 0 )
		mMeshVector[0]->SetLight( light );
	else
		mMesh->SetLight( light );
}

void Body::SetEyePos(const D3DXVECTOR3& eyePos)
{
	if( mMeshVector.size() > 0 )
		mMeshVector[0]->SetEyePos( eyePos );
	else
		mMesh->SetEyePos( eyePos );
}

void Body::SetCubeMap(ID3D10ShaderResourceView* cubeMap)
{
	if( mMeshVector.size() > 0 )
		mMeshVector[0]->SetCubeMap( cubeMap );
	else
		mMesh->SetCubeMap( cubeMap );
}

void Body::SetShadowMap(ID3D10ShaderResourceView* shadowMap)
{
	if( mMeshVector.size() > 0 )
		mMeshVector[0]->SetShadowMap( shadowMap );
	else
		mMesh->SetShadowMap( shadowMap );
}

void Body::EnableCubeMap(bool enable)
{
	if( mMeshVector.size() > 0 )
		mMeshVector[0]->EnableCubeMap( enable );
	else
		mMesh->EnableCubeMap( enable );
}

void Body::UseTangent(bool tangent)
{
	if( mMeshVector.size() > 0 )
		mMeshVector[0]->UseTangent( tangent );
	else
		mMesh->UseTangent( tangent );
}

void Body::UnbindShadowMap()
{
	if( mMeshVector.size() > 0 )
		mMeshVector[0]->UnbindShadowMap();
	else
		mMesh->UnbindShadowMap();
}